#include "rtc.h"

/** converts BCD (Binary Coded Decimal) to decimal
 */
Byte bcd2dec(Byte i)
{
	return (i & 0x0F) + 10 * ((i >> 4) & 0x0F);
}

/** converts decimal to BCD (Binary Coded Decimal)
 */
Byte dec2bcd(Byte i)
{
	return (i % 10) + (((i / 10) % 10) << 4);
}

/** Wait until data in rtc is valid.
 * Data is valid until +/- 240 usec after this function returns,
 * thus a read/write can be done to the RTC within this time period
 */
void rtc_valid()
{
	disable();

	outportb(RTC_ADDR_REG, RTC_STAT_A);
	
	while (inportb(RTC_DATA_REG) & RTC_UIP);
	
	enable();
}

/** Returns rtc data from I/O address add. Doesn't check valid bit
 */
Byte read_rtc(Byte add)
{
	disable();

	outportb(RTC_ADDR_REG, add);
	Byte val = inportb(RTC_DATA_REG);

	enable();
	return val;
}

/** Returns rtc data from I/O address add. Check valid bit
 */
Byte read_rtcv(Byte add)
{
	rtc_valid();
	return read_rtc(add);
}

/** Write value to I/O address add. Doesn't check valid bit
 */
void write_rtc(Byte add, Byte value)
{
	disable();

	outportb(RTC_ADDR_REG, add);
	outportb(RTC_DATA_REG, value);

	enable();
}

/** Write value to I/O address add. Check valid bit
 */
void write_rtcv(Byte add, Byte value)
{
	rtc_valid();
	write_rtc(add, value);
}

/** Read RTC stored time
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_time(RTC_TIME* rt)
{
	rt->hour = bcd2dec(read_rtcv(HOUR));
	rt->min = bcd2dec(read_rtcv(MIN));
	rt->sec = bcd2dec(read_rtcv(SEC));
}

/** Read RTC stored date
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_date(RTC_DATE* rd)
{
	rd->year = bcd2dec(read_rtcv(YEAR));
	rd->month = bcd2dec(read_rtcv(MONTH));
	rd->day = bcd2dec(read_rtcv(MONTH_DAY));
}

/** Read RTC stored alarm
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_alarm(RTC_TIME* rt)
{
	rt->hour = bcd2dec(read_rtcv(HOUR_ALARM));
	rt->min = bcd2dec(read_rtcv(MIN_ALARM));
	rt->sec = bcd2dec(read_rtcv(SEC_ALARM));
}

/** Write alarm to RTC
 *
 * Uses write_rtcv() and dec2bcd()
 */
void rtc_write_alarm(RTC_TIME* rt)
{
	write_rtcv(HOUR_ALARM, dec2bcd(rt->hour));
	write_rtcv(MIN_ALARM, dec2bcd(rt->min));
	write_rtcv(SEC_ALARM, dec2bcd(rt->sec));
}

RTC_TIME subtract_time(RTC_TIME* t_ini, RTC_TIME* t_fim)
{
	unsigned int secs_ini = ((t_ini->hour * 60 + t_ini->min) * 60 + t_ini->sec);
	unsigned int secs_fim = ((t_fim->hour * 60 + t_fim->min) * 60 + t_fim->sec);
	int secs = secs_fim - secs_ini;
	
	if(secs<0)
		secs = (24*60*60) - abs(secs);

	RTC_TIME ret;
	ret.hour = secs/3600;
	ret.min = (secs%3600)/60;
	ret.sec = (secs%3600)%60;
	
	return ret;
}
